package com.zjw.nanyinge.service.impl;

import com.zjw.nanyinge.enums.HandleStatus;
import com.zjw.nanyinge.service.ChatService;
import com.zjw.nanyinge.service.MessageService;
import com.zjw.nanyinge.service.OpenAIChatService;
import com.zjw.nanyinge.vo.req.MessageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

@Slf4j
@Service
public class TextChatServiceImpl implements ChatService {

    @Resource
    private MessageService messageService;
    @Resource
    private OpenAIChatService openAIChatService;
    @Resource
    private CacheManager cacheManager;

    @Override
    public String chat(MessageVO messageVO) {
        String chatCompletion = getChatCompletion(messageVO);
        return chatCompletion == null ? null : buildMessageXML(messageVO, chatCompletion);
    }

    /**
     * 根据新问题获取答案
     *
     * @param messageVO
     * @return
     */
    public String getChatCompletion(MessageVO messageVO) {
        Cache handleCache = cacheManager.getCache("hasHandle");
        Cache sessionCache = cacheManager.getCache("session");
        if (!hasHandle(messageVO, handleCache)) {
            String context = doHandleMessage(messageVO, handleCache, sessionCache);
            if (isSuccess(messageVO, handleCache)) {
                return context;
            }
        }
        return waitCacheData(messageVO, handleCache, sessionCache);
    }

    /**
     * 文本消息如果是等待状态，从缓存中获取数据
     * 1、如果之前的请求处理成功，从缓存中获取数据返回
     * 2、如果之前的请求处理失败，抛异常
     * 3、如果之前的请求处理中，循环sleep等待
     *
     * @param messageVO
     * @param handleCache
     * @param sessionCache
     * @return
     */
    private String waitCacheData(MessageVO messageVO, Cache handleCache, Cache sessionCache) {
        while (true) {
            boolean success = isSuccess(messageVO, handleCache);
            boolean wait = isWait(messageVO, handleCache);
            boolean error = isError(messageVO, handleCache);
            if (success) {
                break;
            }
            if (wait) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            if (error) {
//                throw new RuntimeException("openai请求失败");
                return "服务器压力过大，稍后重试。";
            }
        }
        String newQuestion = "Human:" + messageVO.getContent() + "\n\nAI:";
        String completion = sessionCache.get("TEXT:" + messageVO.getFromUserName(), String.class);
        return getNewMessage(completion, newQuestion);
    }

    /**
     * 核心处理方法，消息数据入库，调用openai接口获取答案
     *
     * @param messageVO
     * @param handleCache
     * @param sessionCache
     * @return
     */
    private String doHandleMessage(MessageVO messageVO, Cache handleCache, Cache sessionCache) {
        String textCacheKey = "TEXT:" + messageVO.getMsgId();
        String newQuestion = "Human:" + messageVO.getContent() + "\n\nAI:";
        handleCache.put(textCacheKey, HandleStatus.WAIT);
        log.info("WeChatController#chat，正在处理：MsgId：{}", messageVO.getMsgId());
        messageService.addMessage(messageVO);
        String allRecord = sessionCache.get("TEXT:" + messageVO.getFromUserName(), String.class);
        if (StringUtils.hasLength(allRecord)) {
            allRecord = allRecord + newQuestion;
        } else {
            allRecord = newQuestion;
        }
        log.info("WeChatController#chat，openai请求参数：{}", allRecord);
        String content = null;
        for (int i = 0; i < 3; i++) {
            try {
                String completion = openAIChatService.createCompletion(allRecord);
                content = parseAndCacheMessage(completion, newQuestion, messageVO, sessionCache);
                log.info("WeChatController#chat，openai返回参数：{}", completion);
                handleCache.put(textCacheKey, HandleStatus.SUCCESS);
                break;
            } catch (Exception e) {
                log.error("openai请求失败：{}", e.getMessage());
                if (i >= 2) {
                    handleCache.put(textCacheKey, HandleStatus.ERROR);
                }else {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
        }
        return content;
    }

    private String buildMessageXML(MessageVO messageVO, String content) {
        return "<xml>" +
                "  <ToUserName><![CDATA[" + messageVO.getFromUserName() + "]]></ToUserName>" +
                "  <FromUserName><![CDATA[" + messageVO.getToUserName() + "]]></FromUserName>" +
                "  <CreateTime>" + System.currentTimeMillis() + "</CreateTime>" +
                "  <MsgType><![CDATA[" + messageVO.getMsgType() + "]]></MsgType>" +
                "  <Content><![CDATA[" + content + "]]></Content>" +
                "</xml>";
    }

    private String parseAndCacheMessage(String completion, String newQuestion, MessageVO messageVO, Cache sessionCache) {
        sessionCache.put("TEXT:" + messageVO.getFromUserName(), completion + "\n\n");
        return getNewMessage(completion, newQuestion);
    }

    private String getNewMessage(String completion, String newQuestion) {
        String[] split = completion.split(newQuestion);
        if (split.length > 1) {
            String content = split[split.length - 1];
            return content.endsWith("\n\n") ? content.substring(0, content.length() - 2) : content;
        }
        return null;
    }

    private boolean hasHandle(MessageVO messageVO, Cache handleCache) {
        String textCacheKey = "TEXT:" + messageVO.getMsgId();
        HandleStatus status = handleCache.get(textCacheKey, HandleStatus.class);
        return status != null;
    }

    private boolean isWait(MessageVO messageVO, Cache handleCache) {
        String textCacheKey = "TEXT:" + messageVO.getMsgId();
        HandleStatus status = handleCache.get(textCacheKey, HandleStatus.class);
        return HandleStatus.WAIT.equals(status);
    }

    private boolean isError(MessageVO messageVO, Cache handleCache) {
        String textCacheKey = "TEXT:" + messageVO.getMsgId();
        HandleStatus status = handleCache.get(textCacheKey, HandleStatus.class);
        return HandleStatus.ERROR.equals(status);
    }

    private boolean isSuccess(MessageVO messageVO, Cache handleCache) {
        String textCacheKey = "TEXT:" + messageVO.getMsgId();
        HandleStatus status = handleCache.get(textCacheKey, HandleStatus.class);
        return HandleStatus.SUCCESS.equals(status);
    }
}
